//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <drvpub.h>
#include "ccamera.h"
#include "ov620.h"
#include "bulverde.h"

#define LIGHT_FREQ_50   1//fluorescent freq: 50hz//sean
//#define LIGHT_FREQ_60   1//fluorescent freq: 60hz//sean

/////////////////////////////////////////////////////////////////
//
// Resolutions
//
/////////////////////////////////////////////////////////////////

// Resolutions that the drvier supports
// VideoWidth & VideoHeight: Used for preview & video
// StillWidth & StillHeight: Same to the value of _CamDrv_Resolution_App
static const CamDrv_Resolution s_CamDrv_Resolution[] =
{
#ifdef INTERPOLATION_TEST
    {176, 144, 176,  144},  // QCIF --Software Interpolation--> QVGA
    {176, 144, 176,  144},  // QCIF --Software Interpolation--> VGA
    {176, 144, 176,  144},  // QCIF --Software Interpolation--> SXGA
#else
    {320, 240, 320,  240},  // QVGA     PHOTO
    {320, 240, 640,  480},  // VGA      PHOTO
    {320, 240, 1280, 960},  // SXGA     PHOTO
#endif
    {176, 144, 176,  144},  // QCIF     VIDEO
};

static BYTE s_command[10];
static BYTE s_brightness[]      = {0x0c,0x00,0x00,0x00,0x00,0x00,0xff,0xff};
static BYTE s_contrast[]        = {0x0d,0x00,0x00,0x00,0x00,0x00,0xff,0xff};
static BYTE s_sharpness[]       = {0x10,0x00,0x00,0x00,0x00,0x00,0xff,0xff};
static BYTE s_saturation[]      = {0x0b,0x00,0x00,0x00,0x00,0x00,0xff,0xff};
static BYTE s_effect[]          = {0x22,0x00,0x00,0x00,0x00,0x00,0xff,0xff};
//static BYTE s_frate[]           = {0x09,0x00,0x00,0x00,0x00,0x00,0xff,0xff};
static BYTE s_whitebalance[]     = {0x14,0x00,0x00,0x00,0x00,0x00,0xff,0xff};


#ifdef DEBUG_CAMERA
EXTERN_C void DumpSensorRegs()
{
    char regValue = 0;
    for(int i = 0; i <= 0xaa; i++)
    {
        I2C_read_byte(i, &regValue);
        kprintf("reg %x: %02x\n", i, 0xff & regValue);
    }
}
#endif

INLINE void DelayMs(int ms)
{
    DzDelay(ms * 1000);
}

/////////////////////////////////////////////////////////////////
//
// We should assure the params of these inline functions are
// appropriate values.
//

// Convert resolution level to hardware register value
INLINE int SensorGetResolutionHardwareValue(int resLevel)
{
#ifdef INTERPOLATION_TEST
    //                   QVGA(From QCIF)  VGA   SXGA   QCIF
    const int resConv[] = {0x04,          0x04, 0x04, 0x04};
#else
    //                   QVGA  VGA   SXGA   QCIF
    const int resConv[] = {0x05, 0x07, 0x10, 0x04};
#endif

    return resConv[resLevel];
}

// Convert zoom level to hardware register value
INLINE int SensorGetZoomHardwareValue(int zoom)
{
    int value;
    if (zoom == 1) value = 0x00;
    else if (zoom == 2) value = 0x01;
    else if (zoom == 4) value = 0x02;
    else /* if (zoom == 8) */ value = 0x03;

    return value;
}

// Convert frame rate to hardware register value
INLINE int SensorGetFrameRateHardwareValue(int frate)
{
    int value;
    if (frate == 30) value = 0x00;
    else if (frate == 20) value = 0x01;
    else if (frate == 15) value = 0x02;
    else if (frate == 12) value = 0x03;
    else if (frate == 10) value = 0x04;
    else if (frate == 7) value = 0x05; // Actually 7.5
    else if (frate == 5) value = 0x06;
    else /* if (frate == 3) */ value = 0x07;

    return value;
}

/////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////
//
// Sensor GPIO
//
/////////////////////////////////////////////////////////////////

EXTERN_C void SensorPowerDown(int powerDown)
{
    if( powerDown == 1 ) {
        // power down
        GPCR2 |= 0x00080000; //83 pin  down
        DzDelay(5000);
        GPSR1 |= 0x00002000; //45 pin  up
    }
    else {
        // power on
        GPSR2 |= 0x00080000; //83 pin  up
        DzDelay(5000);
        GPCR1 |= 0x00002000; //45 pin  down
    }

    DzDelay(5000);
}

EXTERN_C void SensorReset()
{
    //reset
    GPCR0 |= GPIO_bit(22);      /* low */
    DzDelay(5000);
    GPSR0 |= GPIO_bit(22);      /* high */
    DzDelay(5000);
}

/////////////////////////////////////////////////////////////////
//
// Sensor Settings
//
/////////////////////////////////////////////////////////////////

static BOOL SensorDownloadFirmware()
{
    BYTE* data;
    DWORD readLen;
    BYTE *dbuffer;
    int bufferPages;
    BOOL ret = TRUE;
//    USHORT index = 0;

    data = FirmWare;
	readLen = sizeof(FirmWare);
	bufferPages = RoundUp2(readLen, PAGE_SIZE) >> PAGE_SHIFT;

    dbuffer = (BYTE*) Drv_AllocBuffer(bufferPages, 0);
    dbuffer[0] = 0xFE;
    dbuffer[1] = 0xf0;
    dbuffer[2] = 0xc0;
    dbuffer[3] = 0x00;
    dbuffer[4] = 0x00;

	memcpy(&dbuffer[5], &data[0], readLen);
	DBGOUT(kprintf("OV620 firmware len is %d bytes.\n",readLen));

	if (I2C_write_buffer(dbuffer, readLen + 5)) ret = FALSE;

#if 0
	while(ReadLen > 0) {
        if(ReadLen < 64) {
            dbuffer[3] = index & 0x00ff;
            dbuffer[4]= (index & 0xff00) >>8;
            DBGOUT(kprintf("the index high is %d---Low is %d\n",dbuffer[4],dbuffer[3]));
            memcpy(&dbuffer[5],&data[index],ReadLen);
            MSI2C_Master_Polling_Send(dbuffer,ReadLen+5);
            break;
        }
        dbuffer[3] = index & 0x00ff;
        dbuffer[4]= (index & 0xff00) >>8;
        DBGOUT(kprintf("the index high is %d---Low is %d\n",dbuffer[4],dbuffer[3]));
        memcpy(&dbuffer[5],&data[index],64);
           MSI2C_Master_Polling_Send(dbuffer,69);

        index=index+64;
        ReadLen= ReadLen - 64;
    }
#endif

    Drv_FreeBuffer(dbuffer, bufferPages);

    return ret;
}

static BOOL SensorSendInitializeCmd ( )
{
    BYTE command[3];

    command[0] = 0xFE;
    command[1] = 0xF0;
    command[2] = 0x02;

    I2C_write_buffer(command, 3);

    return TRUE;
}

typedef enum {
    SUCCESS,
    FAIL,
    BUSY,
} CmdStatus;

static BOOL SensorGetStatus(BYTE* data, USHORT sizeofbyte)
{
    BYTE REG = 0xFE;
    assert(sizeofbyte == 8 );

    if (I2C_write_buffer(&REG, 1))
    {
        goto Error;
    }

    if (I2C_read_buffer(data, sizeof(BYTE) * sizeofbyte))
    {
        goto Error;
    }

    return TRUE;

Error:
    return FALSE;

}

static CmdStatus SensorParseStatus()
{
    BYTE status[8];

    memset(status, 0, 8);
    SensorGetStatus(status, 8);
    if( (status[0] & 0x80) != 0 )
    {
        DBGOUT(kprintf("+++SensorParseStatus BUSY\n"));
        return BUSY;
    }
    else  if( status[0] == 0x0f)
    {
        DBGOUT(kprintf("+++SensorParseStatus FAIL\n"));
        return FAIL;
    }
    else  if(status[0] == 0x0e)
    {
        DBGOUT(kprintf("+++SensorParseStatus SUCCESS\r\n"));
        return SUCCESS;
    }
    return FAIL;
}

static BOOL SensorWaitStatus(int icount)
{
    int count = icount;
    while(count >0)
    {
        switch(SensorParseStatus())
        {
            case BUSY:
            {
                count --;
                DelayMs(20);
            }
            break;
            case FAIL:
            {
                return FALSE;
            }
            break;
            case SUCCESS:
            {
                return TRUE;
            }
            break;
            default:
                break;
        }

    }
    return FALSE;
}

static BOOL SensorSendCommand(BYTE* data, USHORT sizeofbyte)
{
    assert(sizeofbyte <= 8);

	memset(s_command,0,10);
    s_command[0] = 0xFE;
    memcpy(&s_command[1],data,sizeofbyte);

    if (I2C_write_buffer(s_command, (sizeofbyte+1)*sizeof(BYTE)))
        return FALSE;

    return TRUE;

}


EXTERN_C ECode SensorInit(PCamDrv_Context camera_context)
{
    // Initialize the hardware

    DelayMs(10);
    SensorDownloadFirmware();

    DelayMs(100);
    SensorSendInitializeCmd() ;
	DelayMs(100);
    if(!SensorWaitStatus(5))
        return FALSE;

#define GET_FIRMWARE_VERSION
#ifdef GET_FIRMWARE_VERSION
    // Get firmware version
    BYTE startcmd[8];
    BYTE status[8];
    startcmd[0] = 0x04;
    startcmd[1] = 0x00;
    startcmd[2] = 0x00;
    startcmd[3] = 0x00;
    startcmd[4] = 0x00;
    startcmd[5] = 0x00;
    startcmd[6] = 0xff;
    startcmd[7] = 0xff;
    SensorSendCommand(startcmd, 8);
    SensorGetStatus(status, 8);
    kprintf("FirmVersion: %x %x %x %x %x %x %x %x\n",
        status[0], status[1], status[2], status[3],
        status[4], status[5], status[6], status[7]);
#endif

    // Set default params
    SensorSetBrightness(3);
    SensorSetContrast(3);
    SensorSetSaturation(3);
    SensorSetSharpness(3);
    SensorSetColorMode(CAMERA_CM_COLOR);
    SensorSetWhiteBalance(CAMERA_WB_AUTO);

    return TRUE;
}

BOOL  SensorSetPriview(int stop, int res, int zoom, int frate)
{
    BYTE startcmd[8];

    DBGOUT(kprintf("SensorSetPriview 0x%x, 0x%x 0x%x 0x%x\n", stop, res, zoom ,frate));

    startcmd[0] = 0x30;
    startcmd[1] = stop; // 0x00 start   0x01 stop
    startcmd[2] = res;
    startcmd[3] = frate;
    startcmd[4] = zoom;
    startcmd[5] = 0x01; // YUV422
    startcmd[6] = 0xff;
    startcmd[7] = 0xff;

    SensorSendCommand(startcmd, 8);
    SensorWaitStatus(20);


//    // Set default params
//    SensorSetBrightness(3);
//    SensorSetContrast(3);
//    SensorSetSaturation(3);
//    SensorSetSharpness(3);
//    SensorSetColorMode(CAMERA_CM_COLOR);
//    SensorSetWhiteBalance(CAMERA_WB_AUTO);

    return TRUE;
}

#if 0
static BOOL  SensorSetStillCapture(int res, int zoom)
{
    BYTE startcmd[8];

    DBGOUT(kprintf("SensorSetStillCapture 0x%x 0x%x\n", res, zoom));

    startcmd[0] = 0x07;
    startcmd[1] = 0x00;
    startcmd[2] = res;
    startcmd[3] = 0x00;
    startcmd[4] = zoom;
    startcmd[5] = 0x00;
    startcmd[6] = 0xff;
    startcmd[7] = 0xff;

    SensorSendCommand(startcmd, 8);
    SensorWaitStatus(100);

    return TRUE;
}
#endif

EXTERN_C ECode SensorStartCapture(PCamDrv_Context pCameraCxt)
{
    int resLevel = (pCameraCxt->CaptureStatus == CamDrv_Status_Still)
                ? pCameraCxt->StillResLevel: pCameraCxt->VideoResLevel;
    int zoom = pCameraCxt->AppFormat.Zoom;

    int resRegValue, zoomRegValue;

    // Get hardware register value from user settings
    resRegValue = SensorGetResolutionHardwareValue(resLevel);
    zoomRegValue = SensorGetZoomHardwareValue(zoom);

    if (pCameraCxt->CaptureStatus == CamDrv_Status_Still) {
        // BUGBUG: We cann't use SensorSetStillCapture now.
        //SensorSetStillCapture(resRegValue, zoomRegValue);

        // ONLY FOR QVGA
        int frateRegValue = SensorGetFrameRateHardwareValue(7);
        SensorSetPriview(0x00, resRegValue, zoomRegValue, frateRegValue);
    }
    else {
        // Preview or video mode
        int frate = 15; // Now use 15fps in QVGA or QCIF video capture
#ifdef INTERPOLATION_TEST
        if (resLevel != RES_QCIF) frate = 12;
#endif
        int frateRegValue = SensorGetFrameRateHardwareValue(frate);

        SensorSetPriview(0x00, resRegValue, zoomRegValue, frateRegValue);

    }

    return NOERROR;
}

EXTERN_C void SensorStopCapture(PCamDrv_Context pCameraCxt)
{
    if (pCameraCxt->CaptureStatus == CamDrv_Status_Still) return;

    // Verbiage!
    int resLevel = (pCameraCxt->CaptureStatus == CamDrv_Status_Still)
                ? pCameraCxt->StillResLevel: pCameraCxt->VideoResLevel;
    int zoom = pCameraCxt->AppFormat.Zoom;
    int resRegValue, zoomRegValue, frateRegValue;
    int frate = 15;

#ifdef INTERPOLATION_TEST
    if (resLevel != RES_QCIF) frate = 7;
#endif

    resRegValue = SensorGetResolutionHardwareValue(resLevel);
    zoomRegValue = SensorGetZoomHardwareValue(zoom);
    frateRegValue = SensorGetFrameRateHardwareValue(frate);

    SensorSetPriview(0x01, resRegValue, zoomRegValue, frateRegValue);
}

/////////////////////////////////////////////////////////////////
//
// Sensor Functions
//
/////////////////////////////////////////////////////////////////

EXTERN_C ECode SensorSetBrightness(int level)
{
//  AGC will use brightness level for auto adjustment.
    DBGOUT(kprintf("SensorSetBright %d\n", level));
    assert (level >= 0 && level <= 6);

    s_brightness[2] = level;
    SensorSendCommand(s_brightness, 8);
    SensorWaitStatus(10);

    return NOERROR;
}

EXTERN_C ECode SensorSetContrast(int level)
{
    DBGOUT(kprintf("SensorSetContrast %d\n", level));
    assert (level >= 0 && level <= 6);

    s_contrast[2] = level;
    SensorSendCommand(s_contrast, 8);
    SensorWaitStatus(10);

    return NOERROR;
}

EXTERN_C ECode SensorSetSharpness(int level)
{
    DBGOUT(kprintf("SensorSetSharpness %d\n", level));
    assert (level >= 0 && level <= 6);

    s_sharpness[2] = level;
    SensorSendCommand(s_sharpness, 8);
    SensorWaitStatus(10);

    return NOERROR;
}

EXTERN_C ECode SensorSetSaturation(int level)
{
    DBGOUT(kprintf("SensorSetSaturation %d\n", level));
    assert (level >= 0 && level <= 6);

    s_saturation[2] = level;
    SensorSendCommand(s_saturation, 8);
    SensorWaitStatus(10);

    return NOERROR;
}

EXTERN_C ECode SensorSetWhiteBalance(int WhiteBalance)
{
    ECode ec = NOERROR;
    DBGOUT(kprintf("SensorSetWhiteBalance %d\n", WhiteBalance));

    switch (WhiteBalance) {
    case CAMERA_WB_AUTO:
        s_whitebalance[2] = 0; // default;
        break;

    case CAMERA_WB_DAYLIGHT:
        s_whitebalance[2] = 0x01;
        break;

    case CAMERA_WB_CLOUDY:
        s_whitebalance[2] = 0x02;
        break;

    case CAMERA_WB_FLUORESCENT:
        s_whitebalance[2] = 0x04;
        break;

    case CAMERA_WB_TUNGSTEN:
        s_whitebalance[2] = 0x03;
        break;

#if 0
    case CAMERA_WB_NIGHT:
        break;
#endif

    default:
        ec = E_INVALID_ARGUMENT;
        break;
    }

    SensorSendCommand(s_whitebalance, 8);
	SensorWaitStatus(10);

    return ec;
}

EXTERN_C ECode SensorSetColorMode(int ColorMode)
{
    ECode ec = NOERROR;
    DBGOUT(kprintf("SensorSetColorMode %d\n", ColorMode));

    switch (ColorMode) {
    case CAMERA_CM_COLOR:
        s_effect[2] = 0; // default
        break;

    case CAMERA_CM_NEUTRAL:
        ec = E_NOT_IMPLEMENTED;
        break;

    case CAMERA_CM_VIVID:
        ec = E_NOT_IMPLEMENTED;
        break;

    case CAMERA_CM_SEPIA:
        s_effect[2] = 0x82;
        break;

    case CAMERA_CM_COOL:
        s_effect[2] = 0x83;
        break;

    case CAMERA_CM_REVERSE:
        ec = E_NOT_IMPLEMENTED;
        break;

    case CAMERA_CM_BLACKANDWHITE:
        s_effect[2] = 0x81;
        break;

    default:
        DBGOUT(kprintf("Invalid camera color mode parameters.\n"));
        ec = E_INVALID_ARGUMENT;
        break;
    }

    SensorSendCommand(s_effect, 8);
	SensorWaitStatus(10);

    return ec;
}

// Only support day & night mode now.
EXTERN_C ECode SensorSetSceneMode(int SceneMode)
{

    DBGOUT(kprintf("SensorSetSceneMode %d\n", SceneMode));
    switch(SceneMode)
    {
        case CAMERA_SM_AUTO:
        case CAMERA_SM_PORTRAIT:
        case CAMERA_SM_SPORT:
        case CAMERA_SM_LANDSCAPE:
        case CAMERA_SM_CLOSEUP:
            s_effect[2] = 0x00;
            break;

        case CAMERA_SM_NIGHT:
            s_effect[2] = 0x01;
            break;

        default:
            return E_INVALID_ARGUMENT;
    }

    SensorSendCommand(s_effect, 8);
	SensorWaitStatus(10);

    return NOERROR;
}


/////////////////////////////////////////////////////////////////
//
// Sensor Properties
//
/////////////////////////////////////////////////////////////////

// Get the resolution which the driver uses
EXTERN_C ECode SensorGetResolution(PCamDrv_Context pCameraCxt, int resolution)
{
    unsigned int index = (unsigned int)resolution;

    if (index >=
        sizeof(s_CamDrv_Resolution) / sizeof(CamDrv_Resolution)) {
        //DBGOUT(kprintf(("Unsupported photo quality value.\n"));
        return E_INVALID_ARGUMENT;
    }
    memcpy(&pCameraCxt->Resolution, &s_CamDrv_Resolution[index],
        sizeof(CamDrv_Resolution));

    if (pCameraCxt->bVideo) pCameraCxt->VideoResLevel = resolution; // QCIF, QVGA
    else {
        pCameraCxt->StillResLevel = resolution; // QVGA, VGA, SXGA
        pCameraCxt->VideoResLevel = RES_QVGA; // PREVIEW FOR QVGA, VGA, SXGA
    }

    return NOERROR;
}

EXTERN_C ECode SensorCheckDigitalZoom(PCamDrv_Context pCameraCxt, int zoom)
{
    // Correct zoom check
    if (pCameraCxt->bVideo) {
        // VIDEO MODE
        // QCIF 1x, 2x, 4x
        // QVGA 1x, 2x, 4x
        if (pCameraCxt->VideoResLevel == RES_QCIF) {
            if (zoom == 1 || zoom == 2 || zoom == 4) goto CanSet;
        }
        else if (pCameraCxt->VideoResLevel == RES_QVGA) {
            if (zoom == 1 || zoom == 2 || zoom == 4) goto CanSet;
        }
    }
    else {
        // PHOTO MODE
        // QVGA 1x, 2x, 4x
        // VGA  1x, 2x
        // SXGA 1x
        if (pCameraCxt->StillResLevel == RES_QVGA) {
            if (zoom == 1 || zoom == 2 || zoom == 4) goto CanSet;
        }
        else if (pCameraCxt->StillResLevel == RES_VGA) {
            if (zoom == 1 || zoom == 2) goto CanSet;
        }
        else if (pCameraCxt->StillResLevel == RES_SXGA) {
            if (zoom == 1) goto CanSet;
        }
    }

    return E_INVALID_ARGUMENT; // Incorrect zoom

CanSet:
    return NOERROR;
}
